package com.example.controller;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.example.common.utils.MinioConfig;
import com.example.common.utils.R;
import com.example.common.webSocket.ProgressWebSocket;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.List;

/**
 * 文件上传接口
 */
@RestController
@RequestMapping("/files")
@Api(tags = "文件管理")
public class FileController {

    @Autowired
    private MinioConfig minioConfig;

    // 文件上传存储路径
    private static final String filePath = System.getProperty("user.dir") + "/file/";
    private static final String http = "http://localhost:8080/files/";

    /**
     * 文件上传
     */
    @PostMapping("/upload")
    @ApiOperation(value = "文件上传", notes = "上传文件并返回文件的访问路径")
    public R uploadFile(@RequestParam("file") MultipartFile file) {
        String flag = System.currentTimeMillis() + "";
        String fileName = file.getOriginalFilename();
        Path uploadPath = Paths.get(filePath + flag + "-" + fileName);

        try (InputStream is = file.getInputStream(); // 自动关闭资源
             OutputStream os = Files.newOutputStream(uploadPath)) {

            Files.createDirectories(uploadPath.getParent());

            byte[] buffer = new byte[1024];
            long totalSize = file.getSize();
            long uploadedBytes = 0;
            int bytesRead;

            while ((bytesRead = is.read(buffer)) != -1) {
                os.write(buffer, 0, bytesRead);
                uploadedBytes += bytesRead;
                int progress = (int) ((uploadedBytes * 100) / totalSize);
                ProgressWebSocket.sendProgress(flag, progress);
            }
            ProgressWebSocket.sendProgress(flag, 100);
            System.out.println("正在上传");
            System.out.println(http + flag + "-" + fileName);
            return R.success(new HashMap<String, String>(){{
                put("url", http + flag + "-" + fileName);
                put("flag", flag);
            }});
        } catch (IOException e) {
            return R.error("文件上传失败！");
        }
    }

    @PostMapping("/uploadVoice")
    @ApiOperation(value = "语音文件上传", notes = "上传文件并返回文件的访问路径")
    public R uploadVoice(MultipartFile file) throws Exception {
        // 上传
        String flag = System.currentTimeMillis() + "";
        String fileUrl = minioConfig.putObject(file);
        System.out.println("-----------------------------------"+fileUrl);
        return R.success(new HashMap<String, String>(){{
            put("url", fileUrl);
            put("flag", flag);
        }});
    }

    /**
     * 获取文件
     */
    @GetMapping("/{flag}")
    @ApiOperation(value = "文件下载", notes = "根据文件标识下载文件")
    public void avatarPath(@PathVariable String flag, HttpServletResponse response) {
        if (!FileUtil.isDirectory(filePath)) {
            FileUtil.mkdir(filePath);
        }
        OutputStream os;
        List<String> fileNames = FileUtil.listFileNames(filePath);
        String avatar = fileNames.stream().filter(name -> name.contains(flag)).findAny().orElse("");
        try {
            if (StrUtil.isNotEmpty(avatar)) {
                response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(avatar, "UTF-8"));
                response.setContentType("application/octet-stream");
                byte[] bytes = FileUtil.readBytes(filePath + avatar);
                os = response.getOutputStream();
                os.write(bytes);
                os.flush();
                os.close();
            }
        } catch (Exception e) {
            System.out.println("文件下载失败");
        }
    }

    /**
     * 断点续传下载
     */
    @GetMapping("/resume/{flag}")
    @ApiOperation(value = "断点续传下载", notes = "支持断点续传的文件下载")
    public void downloadWithResume(@PathVariable String flag, HttpServletRequest request, HttpServletResponse response) {
        List<String> fileNames = FileUtil.listFileNames(filePath);
        String fileName = fileNames.stream().filter(name -> name.contains(flag)).findAny().orElse(null);
        if (fileName == null) {
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return;
        }
        File file = new File(filePath + fileName);
        long fileLength = file.length();
        long startPosition = 0;
        long endPosition = fileLength - 1;

        String range = request.getHeader("Range");
        if (range != null && range.startsWith("bytes=")) {
            String[] ranges = range.substring(6).split("-");
            try {
                startPosition = Long.parseLong(ranges[0]);
                if (ranges.length > 1) {
                    endPosition = Long.parseLong(ranges[1]);
                }
            } catch (NumberFormatException ignored) {}
        }

        response.setContentType("application/octet-stream");
        response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
        response.setHeader("Accept-Ranges", "bytes");
        response.setHeader("Content-Length", String.valueOf(endPosition - startPosition + 1));
        response.setHeader("Content-Range", "bytes " + startPosition + "-" + endPosition + "/" + fileLength);
        response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);

        try (RandomAccessFile raf = new RandomAccessFile(file, "r");
             OutputStream os = response.getOutputStream()) {
            byte[] buffer = new byte[1024];
            raf.seek(startPosition);
            int bytesRead;
            long bytesToSend = endPosition - startPosition + 1;
            while ((bytesRead = raf.read(buffer)) != -1 && bytesToSend > 0) {
                os.write(buffer, 0, (int) Math.min(bytesRead, bytesToSend));
                bytesToSend -= bytesRead;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
